package pers.liyan.shi.springcloud.producerticketcore;


import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.rabbitmq.client.Channel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.retry.RecoveryCallback;
import org.springframework.retry.RetryCallback;
import org.springframework.retry.RetryContext;
import org.springframework.retry.backoff.FixedBackOffPolicy;
import org.springframework.retry.policy.SimpleRetryPolicy;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
 * 监听消息队列中购票的信息
 *
 * @author shily
 * @date 2019/4/21 21:38
 */
@Component
public class TicketMessageListener {

    private Logger logger = LoggerFactory.getLogger(this.getClass());
    private ObjectMapper mapper = new ObjectMapper();


    /**
     * 监听消息队列
     *
     * @param message 消息内容
     * @param channel 消息渠道
     * @return void
     * @author shily
     * @date 2019/4/21 21:45
     */
    @RabbitListener(queues = "ticket")
    @RabbitHandler
    public void receiveTicketMessage(Message message, Channel channel) throws IOException {
        String msg = null;

        try {
            msg = new String(message.getBody());
            Map<String, Object> data = mapper.readValue(msg, HashMap.class);
            retry(data);
            //手动应答消息收到
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        } catch (Exception e) {
            logger.error("RabbitMQ 接受消息内容[" + msg + "], 后处理异常: " + e);
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        }

    }


    /**
     * 更新ticket剩余
     *
     * @param data
     * @return void
     * @throws
     * @author shily
     * @date 2019/4/21 22:14
     */
    @Transactional(rollbackFor = Exception.class)
    public void updateTicket(Map<String, Object> data) {
        logger.debug("数据" + data);
        logger.debug("更新ticket数据库, 余票-1");
    }

    /**
     * 失败保存信息
     *
     * @param map 数据
     * @return void
     * @author shily
     * @date 2019/4/21 22:20
     */
    public void failed(Map<String, Object> map) throws JsonProcessingException {
        logger.info("重试次数耗尽...." + mapper.writeValueAsString(map));
    }

    /**
     * 异常重试
     *
     * @param data 输入参数
     * @return void
     * @author shily
     * @date 2019/4/21 21:58
     */
    private void retry(Map<String, Object> data) throws Exception {
        //重试模板
        RetryTemplate retryTemplate = new RetryTemplate();
        //设置重试次数
        SimpleRetryPolicy policy = new SimpleRetryPolicy(3, Collections.singletonMap(Exception.class, true));
        //设置重试间隔时间
        FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
        fixedBackOffPolicy.setBackOffPeriod(100);
        retryTemplate.setRetryPolicy(policy);
        retryTemplate.setBackOffPolicy(fixedBackOffPolicy);
        //编写业务处理代码逻辑
        final RetryCallback<Object, Exception> retryCallback = new RetryCallback<Object, Exception>() {
            @Override
            public Object doWithRetry(RetryContext context) throws Exception {
                logger.debug("第" + (1 + context.getRetryCount()) + "次处理");
                try {
                    updateTicket(data);
                } catch (Exception e) {
                    e.printStackTrace();
                    throw new Exception("捕捉到异常, " + e.getMessage());
                }
                return null;
            }
        };

        //重试次数执行完依然报错, 走以下逻辑
        final RecoveryCallback<Object> recoveryCallback = new RecoveryCallback<Object>() {
            @Override
            public Object recover(RetryContext context) throws Exception {
                failed(data);
                return null;
            }
        };

        retryTemplate.execute(retryCallback, recoveryCallback);
    }


}
